//
// Created by chizuru on 2025/9/8.
//

#include "CameraDevice.h"
#include "FunctionResponseTerminator.h"
#include "OperationResult.h"
#include "xcliberr.h"
#include <vector>
using std::optional;
using std::vector;
using std::cerr;
using std::hex;

CameraDevice::CameraDevice(DeviceComm& comm, std::unique_ptr<IProtocolCodec> codec) : comm(comm), codec(std::move(codec)){}


std::optional<uint32_t> CameraDevice::readRegister(uint16_t addr) {
    return retryPolicy.withRetry([&]() -> optional<uint32_t>{
        // use terminator that accepts either read response length 5 or NACK of 2
        FunctionResponseTerminator term([&](const std::vector<uint8_t>& buf){
            if (buf.empty())
                return false;
            if (buf[0] == 0x06)
                return buf.size() >= 5;
            if (buf[0] == 0x15)
                return buf.size() >= 2;
            return false;
        });

        vector<uint8_t> frame = codec->buildReadFrame(addr);
        vector<uint8_t> resp;
        auto opt = comm.sendFrameAndReadResponse(frame,
                                                 term,
                                                 resp,
                                                 CCD_COMMAND_TRANSMIT_TIME_BOB,
                                                 CCD_COMMAND_TIMEOUT_BOB,
                                                 CCD_COMMAND_INTER_READ_DELAY_BOB);

        if (!opt.success) {
            return optional<uint32_t>{}; // retry
        }
        ProtocolResult pr = codec->parseReadResponse(resp);
        if (!pr.ok) {
            cerr << "Read register 0x" << hex << addr << " NACK " << hex << int(pr.errorCode) << " (" << printCLBErrorMessage(
                    pr.errorCode) << ")\n";
            return optional<uint32_t>{};
        }
        if(auto pi = std::get_if<uint32_t>(&pr.values[0])) {
            return optional<uint32_t>{*pi};
        }
        return optional<uint32_t>{};
    });
}

bool CameraDevice::writeRegister(uint16_t addr, uint32_t value) {
    return retryPolicy.withRetry([&]() -> bool{
        // write response is either 1 byte ACK or 2 bytes NACK
        FunctionResponseTerminator term([&](const std::vector<uint8_t>& buf){
            if (buf.empty())
                return false;
            if (buf[0] == 0x06)
                return true; // ACK
            if (buf[0] == 0x15)
                return buf.size() >= 2; // NACK
            return false;
        });
        vector<uint8_t> frame = codec->buildWriteFrame(addr, {value});
        vector<uint8_t> resp;
        auto opt = comm.sendFrameAndReadResponse(frame,
                                 term,
                                 resp,
                                 CCD_COMMAND_TRANSMIT_TIME_BOB,
                                 CCD_COMMAND_TIMEOUT_BOB,
                                 CCD_COMMAND_INTER_READ_DELAY_BOB);
        if (!opt.success) {
            return false;
        }
        ProtocolResult pr = codec->parseWriteResponse(resp);
        if (!pr.ok) {
            cerr << "Write register 0x" << hex << addr << " failed: " << printCLBErrorMessage(pr.errorCode) << "\n";
            return false;
        }
        return true;
    });
}


void CameraDevice::setRetryPolicy(const RetryPolicy& rp) { retryPolicy = rp; }